Loading R Packages

First, load these packages, since all of them are required to execute further steps. If you can’t load them / are missing one, please use install.packages() to re install or download them, and then run the code. Note that Seurat requires specific versions to work correctly; we typically use v.4.4.0, but the latest version (5.1.0) could also work.

suppressMessages(library(mclust))
suppressMessages(library(MatrixExtra))
suppressMessages(library(ggplot2))
suppressMessages(library(dplyr))
suppressMessages(library(sctransform))
suppressMessages(library(future))
suppressMessages(library(Seurat))
suppressMessages(library(cowplot))
suppressMessages(library(stringr))
suppressMessages(library(patchwork))
suppressMessages(library(Matrix))
suppressMessages(library(Scillus))
suppressMessages(library(MAST))
suppressMessages(library(tidyr))
suppressMessages(library(ggthemes))
suppressMessages(library(magrittr))
suppressMessages(library(SeuratData))
suppressMessages(library(SeuratObject))
suppressMessages(library(devtools))
suppressMessages(library(htmltools))
suppressMessages(library(ScaledMatrix))
suppressMessages(library(sparseMatrixStats))
suppressMessages(library(DelayedMatrixStats))
suppressMessages(library(BPCells))
suppressMessages(library(tidyr))

Loading Seurat Objects

Load the seurat object that contains the single cell data obtained from samples. A seurat object is a dataset that contains the original data of the donors, along with cell types identified, diabetic status, etc. Load the metadata to look at all information contained in this seurat object in various categories. In this case, reharm_SCT_2024Jun18_small is a downsampled version of a processed seurat object containing human pancreatic islet sample data.

reharm_SCT_2024Jun18_small <- readRDS("~/AS/objects/reharm_SCT_2024Jun18_small.rds")

reharm_SCT_2024Jun18_small
An object of class Seurat 
68799 features across 10000 samples within 3 assays 
Active assay: RNA (36601 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 2 other assays present: Protein, SCT
 4 dimensional reductions calculated: pca, harmony, umap, DecontX_NoGroup_umap
reharm_SCT_2024Jun18_small@meta.data

Viewing Cell Types in Seurat Objects

We are interested in viewing different cell types, so we use Idents() and levels(). This shows us different cell types associated with endocrine and inflammatory processes such as alpha, beta, delta-gamma, macrophages, mast cells, etc.

Idents(reharm_SCT_2024Jun18_small) <- "cell_type"
levels(reharm_SCT_2024Jun18_small)
 [1] "Alpha"                      "Alpha_Prolif"               "Beta"                       "Endocrine_MAP2"            
 [5] "Dedifferentiated_Endocrine" "Delta_Gamma"                "Macrophages"                "Mast"                      
 [9] "Lymphocytes"                "B_Cells"                    "Myofibroblasts"             "Pericytes"                 
[13] "Endothelial"                "Schwann"                    "Ductal"                     "Acinar"                    
[17] "LowQuality_Endocrine"       "LowQuality_Endothelial"     "LowQuality_Exo"             "LowQuality"                

Deleting Cell Types Not Relevant to Seurat Objects

We view the cell types present in our large seurat object. We see that there are cell types associated with low quality, and it would be beneficial to the dataset if we removed them. To do this, we follow the code below.

reharm_SCT_2024Jun18_small <- subset(reharm_SCT_2024Jun18_small, idents = c("LowQuality_Endocrine", "LowQuality_Endothelial", "LowQuality_Exo", "LowQuality"), invert = TRUE)

Visualizing Cell Types Using Dimensional Plots.

Now that we have removed low quality cell types, we re-construct a DimPlot with an updated registry of cell types for our reference.

DimPlot(reharm_SCT_2024Jun18_small)+ggtitle("ALL CELL TYPES")

Subsetting Cells of Interest from Seurat Object

Now, we need to make a subset from this data that contains only mast cells, because we are interested in characterizing mast cells in diabetic patients. This generates a new seurat object called mast_cells_cluster. If you want to subset some other type of cells, use the same syntax but edit to include your cell type instead.

mast_cells_cluster <- subset(reharm_SCT_2024Jun18_small, idents = "Mast")
levels(mast_cells_cluster)
[1] "Mast"

Seurat Clusters and How to View Them

Within mast cells, we need to view the different clusters that have been generated under this seurat object. Seurat clusters are usually named 1, 2, 3, etc. that are used to cluster the cells together for more easier organization.

Idents(mast_cells_cluster) <- "seurat_clusters"
levels(mast_cells_cluster)
 [1] "0"  "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"

Single Cell RNA Sequencing of Seurat Clusters

Now, it’s time to prepare this data for scRNA seq analysis. This is done through running a PCA analysis and then running a UMAP analysis to obtain a UMAP plot (can also use TSNE, as they both test linear dimensionality to produce a DimPlot). Then, we use FindNeighbors to compute a shared nearest neighbors (SNN) graph, which helps in clustering.

mast_cells_cluster <- RunPCA(mast_cells_cluster, dims = 1:30, verbose = FALSE) %>%
  RunTSNE(dims = 1:30, verbose = FALSE) %>%
  FindNeighbors(verbose = TRUE) %>%
  FindClusters(resolution = 0.1, verbose = TRUE)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 2266
Number of edges: 79677

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9346
Number of communities: 3
Elapsed time: 0 seconds

Saving the Analyzed Cells as a new Seurat Object

We have now obtained a seurat object with our required scRNA seq parameters. Save this object using saveRDS().

saveRDS(mast_cells_cluster, "mast_cells_cluster.rds")

Visualizing This Object as a TSNE Dimensional Plot

Here’s a TSNE plot to exhibit our clustering results:

TSNEPlot(mast_cells_cluster, group.by = "seurat_clusters")

And a TSNE plot to show the same, grouped by heterogenous donors, and conditions:

TSNEPlot(mast_cells_cluster, group.by = "Donor")

TSNEPlot(mast_cells_cluster, group.by = "Condition")

Finding Markers for Differential Expressed Genes

Now we need to find marker genes for this processed mast cell cluster object under the 3 clusters identified. Obtaining these marker genes will help us downstream in seeing which genes are expressed when and why. They are called differential expressed genes (DEG).

Idents(mast_cells_cluster) <- "seurat_clusters"
mast_cell_markers <-  Seurat::FindAllMarkers(mast_cells_cluster, only.pos = FALSE, test.use = "roc")

Save markers as table.

write.csv(mast_cell_markers, "mast_cell_markers_clean.csv")
head(mast_cell_markers_clean)

Classifying DEG Markers Based on Diabetic Conditions:

Change Idents() to look at differential expressed genes by condition, because we want to view the marker genes we obtained before based on Lean condition (which is non-diabetic), and T2D condition. We ignore Obese group because it contains both diabetic and non-diabetic samples, which has shown to produce discrepancy in results.

Idents(mast_cells_cluster) <- "Condition"
levels(mast_cells_cluster)
[1] "Lean"   "Obese"  "PreT2D" "T2D"   

Visualizing Such Markers through TSNE Dimensional Plots

Let’s now view the analysis we’ve done using TSNEPlot. We first obtain a general plot of all conditions for reference. You can also do this using UMAPPlot or DimPlot.

TSNEPlot(mast_cells_cluster,group.by = "Condition")

Subsetting Markers into Diabetic Conditions of Interest

We further subset the seurat object mast_cells_cluster based on the conditions we want, which are lean and T2D. We then reprocess condition_subset into clusters for accurate visualization.

mast_cells_cluster@graphs <- list()
condition_subset <- subset(mast_cells_cluster, idents = c("Lean", "T2D"))
condition_subset <- RunPCA(condition_subset, dims = 1:30, verbose = FALSE) %>%
  RunTSNE(dims = 1:30, verbose = FALSE) %>%
  FindNeighbors(verbose = TRUE) %>%
  FindClusters(resolution = 0.1, verbose = TRUE)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1410
Number of edges: 48291

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9153
Number of communities: 3
Elapsed time: 0 seconds
TSNEPlot(condition_subset)+ggtitle("Lean and T2D Conditions")

Finding DEGs Based on these Specific Diabetic Conditions

Find DEGs between Lean and T2D conditions.

condition_subset <- Seurat::FindAllMarkers(condition_subset, only.pos = FALSE, test.use = "roc")

This can be saved both as a CSV for later use on Excel.

write.csv(condition_subset, "condition_subset.csv")

Organizing DEG Lists

In this final step, the DEG list we’ve obtained, condition_subset, needs to be organized better for easier analysis. We organize it in two ways. First is based on average log2FC, because increasing positive log fold change denotes up regulation in genes. Second is based on removing discrepant genes such as mitochondrial (MT) and ribosome (RP) genes using the function !grepl.

condition_subset$genes <- rownames(condition_subset)
condition_subset <- dplyr::filter(condition_subset, !grepl("MT-", condition_subset$genes))
condition_subset <- dplyr::filter(condition_subset, !grepl("RP", condition_subset$genes))
sorted_mast_cells_condition_subset <- condition_subset[order(condition_subset$avg_log2FC, decreasing = TRUE), ]

head(sorted_mast_cells_condition_subset)

Saving and Viewing an Organized DEG Table:

After organizing through average log2FC and discrepant genes, we save as csv file and export to Excel for further analysis.

View(sorted_mast_cells_condition_subset)
write.csv(sorted_mast_cells_condition_subset, "sorted_mast_cells_condition_subset.csv")
LS0tCnRpdGxlOiAic2NSTkEgU2VxdWVuY2luZyBBbmFseXNpcyBvZiBNYXN0IENlbGxzIGluIERpYWJldGljIFBhbmNyZWF0aWMgTWljcm9lbnZpcm9ubWVudCIKYXV0aG9yOiAiQWtpbGVzaCBTaGFua2FyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdApEYXRlOiBKdWx5IDIwMjQKZWRpdG9yX29wdGlvbnM6CiAgbWFya2Rvd246CiAgICB3cmFwOiA3MgotLS0KIyMjIExvYWRpbmcgUiBQYWNrYWdlcwpGaXJzdCwgbG9hZCB0aGVzZSBwYWNrYWdlcywgc2luY2UgYWxsIG9mIHRoZW0gYXJlIHJlcXVpcmVkIHRvIGV4ZWN1dGUgZnVydGhlciBzdGVwcy4gSWYgeW91IGNhbid0IGxvYWQgdGhlbSAvIGFyZSBtaXNzaW5nIG9uZSwgcGxlYXNlIHVzZSBpbnN0YWxsLnBhY2thZ2VzKCkgdG8gcmUgaW5zdGFsbCBvciBkb3dubG9hZCB0aGVtLCBhbmQgdGhlbiBydW4gdGhlIGNvZGUuIE5vdGUgdGhhdCBTZXVyYXQgcmVxdWlyZXMgc3BlY2lmaWMgdmVyc2lvbnMgdG8gd29yayBjb3JyZWN0bHk7IHdlIHR5cGljYWxseSB1c2Ugdi40LjQuMCwgYnV0IHRoZSBsYXRlc3QgdmVyc2lvbiAoNS4xLjApIGNvdWxkIGFsc28gd29yay4KYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShtY2x1c3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTWF0cml4RXh0cmEpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2dwbG90MikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzY3RyYW5zZm9ybSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShmdXR1cmUpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoU2V1cmF0KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGNvd3Bsb3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RyaW5ncikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShwYXRjaHdvcmspKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTWF0cml4KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFNjaWxsdXMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTUFTVCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3RoZW1lcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShtYWdyaXR0cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShTZXVyYXREYXRhKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFNldXJhdE9iamVjdCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkZXZ0b29scykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShodG1sdG9vbHMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoU2NhbGVkTWF0cml4KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHNwYXJzZU1hdHJpeFN0YXRzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KERlbGF5ZWRNYXRyaXhTdGF0cykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShCUENlbGxzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHRpZHlyKSkKYGBgCgoKIyMjIExvYWRpbmcgU2V1cmF0IE9iamVjdHMgCkxvYWQgdGhlIHNldXJhdCBvYmplY3QgdGhhdCBjb250YWlucyB0aGUgc2luZ2xlIGNlbGwgZGF0YSBvYnRhaW5lZCBmcm9tIHNhbXBsZXMuIEEgc2V1cmF0IG9iamVjdCBpcyBhIGRhdGFzZXQgdGhhdCBjb250YWlucyB0aGUgb3JpZ2luYWwgZGF0YSBvZiB0aGUgZG9ub3JzLCBhbG9uZyB3aXRoIGNlbGwgdHlwZXMgaWRlbnRpZmllZCwgZGlhYmV0aWMgc3RhdHVzLCBldGMuIExvYWQgdGhlIG1ldGFkYXRhIHRvIGxvb2sgYXQgYWxsIGluZm9ybWF0aW9uIGNvbnRhaW5lZCBpbiB0aGlzIHNldXJhdCBvYmplY3QgaW4gdmFyaW91cyBjYXRlZ29yaWVzLiBJbiB0aGlzIGNhc2UsIHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsIGlzIGEgZG93bnNhbXBsZWQgdmVyc2lvbiBvZiBhIHByb2Nlc3NlZCBzZXVyYXQgb2JqZWN0IGNvbnRhaW5pbmcgaHVtYW4gcGFuY3JlYXRpYyBpc2xldCBzYW1wbGUgZGF0YS4gCmBgYHtyfQpyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCA8LSByZWFkUkRTKCJ+L0FTL29iamVjdHMvcmVoYXJtX1NDVF8yMDI0SnVuMThfc21hbGwucmRzIikKCnJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsCgpyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbEBtZXRhLmRhdGEKYGBgCgojIyMgVmlld2luZyBDZWxsIFR5cGVzIGluIFNldXJhdCBPYmplY3RzCldlIGFyZSBpbnRlcmVzdGVkIGluIHZpZXdpbmcgZGlmZmVyZW50IGNlbGwgdHlwZXMsIHNvIHdlIHVzZSBJZGVudHMoKSBhbmQgbGV2ZWxzKCkuIFRoaXMgc2hvd3MgdXMgZGlmZmVyZW50IGNlbGwgdHlwZXMgYXNzb2NpYXRlZCB3aXRoIGVuZG9jcmluZSBhbmQgaW5mbGFtbWF0b3J5IHByb2Nlc3NlcyBzdWNoIGFzIGFscGhhLCBiZXRhLCBkZWx0YS1nYW1tYSwgbWFjcm9waGFnZXMsIG1hc3QgY2VsbHMsIGV0Yy4KYGBge3J9CklkZW50cyhyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCkgPC0gImNlbGxfdHlwZSIKbGV2ZWxzKHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsKQpgYGAKCgojIyMgRGVsZXRpbmcgQ2VsbCBUeXBlcyBOb3QgUmVsZXZhbnQgdG8gU2V1cmF0IE9iamVjdHMKV2UgdmlldyB0aGUgY2VsbCB0eXBlcyBwcmVzZW50IGluIG91ciBsYXJnZSBzZXVyYXQgb2JqZWN0LiBXZSBzZWUgdGhhdCB0aGVyZSBhcmUgY2VsbCB0eXBlcyBhc3NvY2lhdGVkIHdpdGggbG93IHF1YWxpdHksIGFuZCBpdCB3b3VsZCBiZSBiZW5lZmljaWFsIHRvIHRoZSBkYXRhc2V0IGlmIHdlIHJlbW92ZWQgdGhlbS4gVG8gZG8gdGhpcywgd2UgZm9sbG93IHRoZSBjb2RlIGJlbG93LgpgYGB7cn0KcmVoYXJtX1NDVF8yMDI0SnVuMThfc21hbGwgPC0gc3Vic2V0KHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsLCBpZGVudHMgPSBjKCJMb3dRdWFsaXR5X0VuZG9jcmluZSIsICJMb3dRdWFsaXR5X0VuZG90aGVsaWFsIiwgIkxvd1F1YWxpdHlfRXhvIiwgIkxvd1F1YWxpdHkiKSwgaW52ZXJ0ID0gVFJVRSkKYGBgCgoKIyMjIFZpc3VhbGl6aW5nIENlbGwgVHlwZXMgVXNpbmcgRGltZW5zaW9uYWwgUGxvdHMuCk5vdyB0aGF0IHdlIGhhdmUgcmVtb3ZlZCBsb3cgcXVhbGl0eSBjZWxsIHR5cGVzLCB3ZSByZS1jb25zdHJ1Y3QgYSBEaW1QbG90IHdpdGggYW4gdXBkYXRlZCByZWdpc3RyeSBvZiBjZWxsIHR5cGVzIGZvciBvdXIgcmVmZXJlbmNlLgpgYGB7cn0KRGltUGxvdChyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCkrZ2d0aXRsZSgiQUxMIENFTEwgVFlQRVMiKQpgYGAKIyMjIFN1YnNldHRpbmcgQ2VsbHMgb2YgSW50ZXJlc3QgZnJvbSBTZXVyYXQgT2JqZWN0Ck5vdywgd2UgbmVlZCB0byBtYWtlIGEgc3Vic2V0IGZyb20gdGhpcyBkYXRhIHRoYXQgY29udGFpbnMgb25seSBtYXN0IGNlbGxzLCBiZWNhdXNlIHdlIGFyZSBpbnRlcmVzdGVkIGluIGNoYXJhY3Rlcml6aW5nIG1hc3QgY2VsbHMgaW4gZGlhYmV0aWMgcGF0aWVudHMuIFRoaXMgZ2VuZXJhdGVzIGEgbmV3IHNldXJhdCBvYmplY3QgY2FsbGVkIG1hc3RfY2VsbHNfY2x1c3Rlci4gSWYgeW91IHdhbnQgdG8gc3Vic2V0IHNvbWUgb3RoZXIgdHlwZSBvZiBjZWxscywgdXNlIHRoZSBzYW1lIHN5bnRheCBidXQgZWRpdCB0byBpbmNsdWRlIHlvdXIgY2VsbCB0eXBlIGluc3RlYWQuCmBgYHtyfQptYXN0X2NlbGxzX2NsdXN0ZXIgPC0gc3Vic2V0KHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsLCBpZGVudHMgPSAiTWFzdCIpCmxldmVscyhtYXN0X2NlbGxzX2NsdXN0ZXIpCmBgYAoKIyMjIFNldXJhdCBDbHVzdGVycyBhbmQgSG93IHRvIFZpZXcgVGhlbQpXaXRoaW4gbWFzdCBjZWxscywgd2UgbmVlZCB0byB2aWV3IHRoZSBkaWZmZXJlbnQgY2x1c3RlcnMgdGhhdCBoYXZlIGJlZW4gZ2VuZXJhdGVkIHVuZGVyIHRoaXMgc2V1cmF0IG9iamVjdC4gU2V1cmF0IGNsdXN0ZXJzIGFyZSB1c3VhbGx5IG5hbWVkIDEsIDIsIDMsIGV0Yy4gdGhhdCBhcmUgdXNlZCB0byBjbHVzdGVyIHRoZSBjZWxscyB0b2dldGhlciBmb3IgbW9yZSBlYXNpZXIgb3JnYW5pemF0aW9uLgpgYGB7cn0KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gInNldXJhdF9jbHVzdGVycyIKbGV2ZWxzKG1hc3RfY2VsbHNfY2x1c3RlcikKYGBgCgojIyMgU2luZ2xlIENlbGwgUk5BIFNlcXVlbmNpbmcgb2YgU2V1cmF0IENsdXN0ZXJzCk5vdywgaXQncyB0aW1lIHRvIHByZXBhcmUgdGhpcyBkYXRhIGZvciBzY1JOQSBzZXEgYW5hbHlzaXMuIFRoaXMgaXMgZG9uZSB0aHJvdWdoIHJ1bm5pbmcgYSBQQ0EgYW5hbHlzaXMgYW5kIHRoZW4gcnVubmluZyBhIFVNQVAgYW5hbHlzaXMgdG8gb2J0YWluIGEgVU1BUCBwbG90IChjYW4gYWxzbyB1c2UgVFNORSwgYXMgdGhleSBib3RoIHRlc3QgbGluZWFyIGRpbWVuc2lvbmFsaXR5IHRvIHByb2R1Y2UgYSBEaW1QbG90KS4gVGhlbiwgd2UgdXNlIEZpbmROZWlnaGJvcnMgdG8gY29tcHV0ZSBhIHNoYXJlZCBuZWFyZXN0IG5laWdoYm9ycyAoU05OKSBncmFwaCwgd2hpY2ggaGVscHMgaW4gY2x1c3RlcmluZy4KYGBge3J9Cm1hc3RfY2VsbHNfY2x1c3RlciA8LSBSdW5QQ0EobWFzdF9jZWxsc19jbHVzdGVyLCBkaW1zID0gMTozMCwgdmVyYm9zZSA9IEZBTFNFKSAlPiUKICBSdW5UU05FKGRpbXMgPSAxOjMwLCB2ZXJib3NlID0gRkFMU0UpICU+JQogIEZpbmROZWlnaGJvcnModmVyYm9zZSA9IFRSVUUpICU+JQogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4xLCB2ZXJib3NlID0gVFJVRSkKYGBgCgoKIyMjIFNhdmluZyB0aGUgQW5hbHl6ZWQgQ2VsbHMgYXMgYSBuZXcgU2V1cmF0IE9iamVjdApXZSBoYXZlIG5vdyBvYnRhaW5lZCBhIHNldXJhdCBvYmplY3Qgd2l0aCBvdXIgcmVxdWlyZWQgc2NSTkEgc2VxIHBhcmFtZXRlcnMuIFNhdmUgdGhpcyBvYmplY3QgdXNpbmcgc2F2ZVJEUygpLgpgYGB7cn0Kc2F2ZVJEUyhtYXN0X2NlbGxzX2NsdXN0ZXIsICJtYXN0X2NlbGxzX2NsdXN0ZXIucmRzIikKYGBgCgoKCiMjIyBWaXN1YWxpemluZyBUaGlzIE9iamVjdCBhcyBhIFRTTkUgRGltZW5zaW9uYWwgUGxvdApIZXJlJ3MgYSBUU05FIHBsb3QgdG8gZXhoaWJpdCBvdXIgY2x1c3RlcmluZyByZXN1bHRzOgpgYGB7cn0KVFNORVBsb3QobWFzdF9jZWxsc19jbHVzdGVyLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQpgYGAKCgpBbmQgYSBUU05FIHBsb3QgdG8gc2hvdyB0aGUgc2FtZSwgZ3JvdXBlZCBieSBoZXRlcm9nZW5vdXMgZG9ub3JzLCBhbmQgY29uZGl0aW9uczoKYGBge3J9ClRTTkVQbG90KG1hc3RfY2VsbHNfY2x1c3RlciwgZ3JvdXAuYnkgPSAiRG9ub3IiKQpgYGAKCgpgYGB7cn0KVFNORVBsb3QobWFzdF9jZWxsc19jbHVzdGVyLCBncm91cC5ieSA9ICJDb25kaXRpb24iKQpgYGAKCgojIyMgRmluZGluZyBNYXJrZXJzIGZvciBEaWZmZXJlbnRpYWwgRXhwcmVzc2VkIEdlbmVzCk5vdyB3ZSBuZWVkIHRvIGZpbmQgbWFya2VyIGdlbmVzIGZvciB0aGlzIHByb2Nlc3NlZCBtYXN0IGNlbGwgY2x1c3RlciBvYmplY3QgdW5kZXIgdGhlIDMgY2x1c3RlcnMgaWRlbnRpZmllZC4gT2J0YWluaW5nIHRoZXNlIG1hcmtlciBnZW5lcyB3aWxsIGhlbHAgdXMgZG93bnN0cmVhbSBpbiBzZWVpbmcgd2hpY2ggZ2VuZXMgYXJlIGV4cHJlc3NlZCB3aGVuIGFuZCB3aHkuIFRoZXkgYXJlIGNhbGxlZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIChERUcpLgpgYGB7ciwgZWNobz1UUlVFLCByZXN1bHRzPSdoaWRlJ30KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gInNldXJhdF9jbHVzdGVycyIKbWFzdF9jZWxsX21hcmtlcnMgPC0gIFNldXJhdDo6RmluZEFsbE1hcmtlcnMobWFzdF9jZWxsc19jbHVzdGVyLCBvbmx5LnBvcyA9IEZBTFNFLCB0ZXN0LnVzZSA9ICJyb2MiKQpgYGAKCgpTYXZlIG1hcmtlcnMgYXMgdGFibGUuCmBgYHtyfQp3cml0ZS5jc3YobWFzdF9jZWxsX21hcmtlcnMsICJtYXN0X2NlbGxfbWFya2Vyc19jbGVhbi5jc3YiKQpoZWFkKG1hc3RfY2VsbF9tYXJrZXJzX2NsZWFuKQpgYGAKCgoKIyMjIENsYXNzaWZ5aW5nIERFRyBNYXJrZXJzIEJhc2VkIG9uIERpYWJldGljIENvbmRpdGlvbnM6CkNoYW5nZSBJZGVudHMoKSB0byBsb29rIGF0IGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgYnkgY29uZGl0aW9uLCBiZWNhdXNlIHdlIHdhbnQgdG8gdmlldyB0aGUgbWFya2VyIGdlbmVzIHdlIG9idGFpbmVkIGJlZm9yZSBiYXNlZCBvbiBMZWFuIGNvbmRpdGlvbiAod2hpY2ggaXMgbm9uLWRpYWJldGljKSwgYW5kIFQyRCBjb25kaXRpb24uIFdlIGlnbm9yZSBPYmVzZSBncm91cCBiZWNhdXNlIGl0IGNvbnRhaW5zIGJvdGggZGlhYmV0aWMgYW5kIG5vbi1kaWFiZXRpYyBzYW1wbGVzLCB3aGljaCBoYXMgc2hvd24gdG8gcHJvZHVjZSBkaXNjcmVwYW5jeSBpbiByZXN1bHRzLgpgYGB7cn0KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gIkNvbmRpdGlvbiIKbGV2ZWxzKG1hc3RfY2VsbHNfY2x1c3RlcikKYGBgCgoKCiMjIyBWaXN1YWxpemluZyBTdWNoIE1hcmtlcnMgdGhyb3VnaCBUU05FIERpbWVuc2lvbmFsIFBsb3RzCkxldCdzIG5vdyB2aWV3IHRoZSBhbmFseXNpcyB3ZSd2ZSBkb25lIHVzaW5nIFRTTkVQbG90LiBXZSBmaXJzdCBvYnRhaW4gYSBnZW5lcmFsIHBsb3Qgb2YgYWxsIGNvbmRpdGlvbnMgZm9yIHJlZmVyZW5jZS4gWW91IGNhbiBhbHNvIGRvIHRoaXMgdXNpbmcgVU1BUFBsb3Qgb3IgRGltUGxvdC4KYGBge3J9ClRTTkVQbG90KG1hc3RfY2VsbHNfY2x1c3Rlcixncm91cC5ieSA9ICJDb25kaXRpb24iKQpgYGAKCgoKIyMjIFN1YnNldHRpbmcgTWFya2VycyBpbnRvIERpYWJldGljIENvbmRpdGlvbnMgb2YgSW50ZXJlc3QKV2UgZnVydGhlciBzdWJzZXQgdGhlIHNldXJhdCBvYmplY3QgbWFzdF9jZWxsc19jbHVzdGVyIGJhc2VkIG9uIHRoZSBjb25kaXRpb25zIHdlIHdhbnQsIHdoaWNoIGFyZSBsZWFuIGFuZCBUMkQuIFdlIHRoZW4gcmVwcm9jZXNzIGNvbmRpdGlvbl9zdWJzZXQgaW50byBjbHVzdGVycyBmb3IgYWNjdXJhdGUgdmlzdWFsaXphdGlvbi4KYGBge3J9Cm1hc3RfY2VsbHNfY2x1c3RlckBncmFwaHMgPC0gbGlzdCgpCmNvbmRpdGlvbl9zdWJzZXQgPC0gc3Vic2V0KG1hc3RfY2VsbHNfY2x1c3RlciwgaWRlbnRzID0gYygiTGVhbiIsICJUMkQiKSkKY29uZGl0aW9uX3N1YnNldCA8LSBSdW5QQ0EoY29uZGl0aW9uX3N1YnNldCwgZGltcyA9IDE6MzAsIHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgUnVuVFNORShkaW1zID0gMTozMCwgdmVyYm9zZSA9IEZBTFNFKSAlPiUKICBGaW5kTmVpZ2hib3JzKHZlcmJvc2UgPSBUUlVFKSAlPiUKICBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSwgdmVyYm9zZSA9IFRSVUUpClRTTkVQbG90KGNvbmRpdGlvbl9zdWJzZXQpK2dndGl0bGUoIkxlYW4gYW5kIFQyRCBDb25kaXRpb25zIikKYGBgCgojIyMgRmluZGluZyBERUdzIEJhc2VkIG9uIHRoZXNlIFNwZWNpZmljIERpYWJldGljIENvbmRpdGlvbnMKRmluZCBERUdzIGJldHdlZW4gTGVhbiBhbmQgVDJEIGNvbmRpdGlvbnMuCmBgYHtyLCBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQpjb25kaXRpb25fc3Vic2V0IDwtIFNldXJhdDo6RmluZEFsbE1hcmtlcnMoY29uZGl0aW9uX3N1YnNldCwgb25seS5wb3MgPSBGQUxTRSwgdGVzdC51c2UgPSAicm9jIikKYGBgCgpUaGlzIGNhbiBiZSBzYXZlZCBib3RoIGFzIGEgQ1NWIGZvciBsYXRlciB1c2Ugb24gRXhjZWwuCmBgYHtyfQp3cml0ZS5jc3YoY29uZGl0aW9uX3N1YnNldCwgImNvbmRpdGlvbl9zdWJzZXQuY3N2IikKYGBgCgoKIyMjIE9yZ2FuaXppbmcgREVHIExpc3RzCkluIHRoaXMgZmluYWwgc3RlcCwgdGhlIERFRyBsaXN0IHdlJ3ZlIG9idGFpbmVkLCBjb25kaXRpb25fc3Vic2V0LCBuZWVkcyB0byBiZSBvcmdhbml6ZWQgYmV0dGVyIGZvciBlYXNpZXIgYW5hbHlzaXMuIFdlIG9yZ2FuaXplIGl0IGluIHR3byB3YXlzLiBGaXJzdCBpcyBiYXNlZCBvbiBhdmVyYWdlIGxvZzJGQywgYmVjYXVzZSBpbmNyZWFzaW5nIHBvc2l0aXZlIGxvZyBmb2xkIGNoYW5nZSBkZW5vdGVzIHVwIHJlZ3VsYXRpb24gaW4gZ2VuZXMuIFNlY29uZCBpcyBiYXNlZCBvbiByZW1vdmluZyBkaXNjcmVwYW50IGdlbmVzIHN1Y2ggYXMgbWl0b2Nob25kcmlhbCAoTVQpIGFuZCByaWJvc29tZSAoUlApIGdlbmVzIHVzaW5nIHRoZSBmdW5jdGlvbiAhZ3JlcGwuCmBgYHtyfQpjb25kaXRpb25fc3Vic2V0JGdlbmVzIDwtIHJvd25hbWVzKGNvbmRpdGlvbl9zdWJzZXQpCmNvbmRpdGlvbl9zdWJzZXQgPC0gZHBseXI6OmZpbHRlcihjb25kaXRpb25fc3Vic2V0LCAhZ3JlcGwoIk1ULSIsIGNvbmRpdGlvbl9zdWJzZXQkZ2VuZXMpKQpjb25kaXRpb25fc3Vic2V0IDwtIGRwbHlyOjpmaWx0ZXIoY29uZGl0aW9uX3N1YnNldCwgIWdyZXBsKCJSUCIsIGNvbmRpdGlvbl9zdWJzZXQkZ2VuZXMpKQpzb3J0ZWRfbWFzdF9jZWxsc19jb25kaXRpb25fc3Vic2V0IDwtIGNvbmRpdGlvbl9zdWJzZXRbb3JkZXIoY29uZGl0aW9uX3N1YnNldCRhdmdfbG9nMkZDLCBkZWNyZWFzaW5nID0gVFJVRSksIF0KCmhlYWQoc29ydGVkX21hc3RfY2VsbHNfY29uZGl0aW9uX3N1YnNldCkKYGBgCgojIyMgU2F2aW5nIGFuZCBWaWV3aW5nIGFuIE9yZ2FuaXplZCBERUcgVGFibGU6CkFmdGVyIG9yZ2FuaXppbmcgdGhyb3VnaCBhdmVyYWdlIGxvZzJGQyBhbmQgZGlzY3JlcGFudCBnZW5lcywgd2Ugc2F2ZSBhcyBjc3YgZmlsZSBhbmQgZXhwb3J0IHRvIEV4Y2VsIGZvciBmdXJ0aGVyIGFuYWx5c2lzLgpgYGB7cn0KVmlldyhzb3J0ZWRfbWFzdF9jZWxsc19jb25kaXRpb25fc3Vic2V0KQp3cml0ZS5jc3Yoc29ydGVkX21hc3RfY2VsbHNfY29uZGl0aW9uX3N1YnNldCwgInNvcnRlZF9tYXN0X2NlbGxzX2NvbmRpdGlvbl9zdWJzZXQuY3N2IikKYGBgCg==